package com.magina.nety.redisCache;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RList;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.redisson.api.SortOrder;
import org.redisson.client.codec.Codec;
import org.redisson.codec.JsonJacksonCodec;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 初始作者： Magina.Hu</br>
 * 创建日期： 2019/7/25</br>
 * 功能说明：  <br/>
 * =================================================<br/>
 * 修改记录：<br/>
 * 修改作者 日期 修改内容<br/>
 * ================================================<br/>
 * Copyright (c) 2019-2020 .All rights reserved.<br/>
 */
@Slf4j
@Component
@Aspect
public class PageRedisAspect {

    public static final AtomicInteger countPageArg = new AtomicInteger(0);
    public static final AtomicInteger countSortArg  = new AtomicInteger(0);

    @Resource
    private RedissonClient reportRedisson;

    public PageRedisAspect() {
        log.info("PageRedisAspect Ready!");
    }

    /**
     * 切点
     */
    @Pointcut("@annotation(com.magina.nety.redisCache.PageRedis)")
    public void methodCachePointcut() {
    }

    /**
     * 切面
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around(value = "methodCachePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        initCount();
        Object object = null;
        Integer pageNum = 1;
        Integer pageSize = 10;
        String sortStr = null;
        SortOrder direction = null;
        PageRedis pageRedis = this.getPageRedis(point);
        String keyJson = "";
        if (StrUtil.isNotBlank(pageRedis.key()) && pageRedis.key().contains("#")) {
            keyJson = generateKeyBySpEL(pageRedis.key(), point);
            //keyJson需要md5
            String codeStr = DigestUtils.md5DigestAsHex(keyJson.getBytes());
        } else {

        }
        PageRequest pageRequest = null;
        Object[] argObjects = point.getArgs();
        if (null != argObjects && argObjects.length > 0) {
            for (Object o : argObjects) {
                if (o instanceof PageRequest) {
                    //todo 参数注解
                    countPageArg.getAndIncrement();//递增
                    pageRequest = (PageRequest) o;
                    for (Sort.Order order : pageRequest.getSort()) {
                        countSortArg.getAndIncrement();
                        sortStr = order.getProperty();
                        direction = SortOrder.DESC.name().equalsIgnoreCase(order.getDirection().name()) ? SortOrder.DESC
                                : SortOrder.ASC;
                    }
                }
            }
        } else {
            //默认分页参数.不带排序
        }
        if (countPageArg.get() > 1) {
            log.warn("page-arg exist more than 1 ,pick the last one!");
        }
        if (countSortArg.get() > 1) {
            log.warn("sort-arg exist more than 1 ,pick the last one!");
        }
        pageNum = pageRequest.getPageNumber();//第几页
        pageSize = pageRequest.getPageSize();
        Integer startIndex = (pageNum - 1) * pageSize;
            Integer endIndex = pageSize*pageNum -1;
        Codec jsonJacksonCodec = new JsonJacksonCodec(pageRedis.keyClass().getClassLoader());
        RList<Object> rList = reportRedisson.getList(keyJson, jsonJacksonCodec);

        long start = System.currentTimeMillis();
        List<Object> rListData = rList.readSort(sortStr, direction, startIndex, pageSize);
        log.info("{} get data from redis time : {} ", point.getSignature().getName(), (System.currentTimeMillis() - start));
        if (ObjectUtil.isNotNull(rListData) && CollectionUtil.isNotEmpty(rListData)) {
            Page page = new PageImpl(rListData,pageRequest,rList.size());
            System.out.println(rList.size());
            initCount();
            return page;
        } else {
            object = point.proceed();
            if (object instanceof Collection) {
                rList.addAll((Collection) object);
                List resultList = (List) object;
                return new PageImpl<>(resultList,pageRequest,resultList.size());
            } else if (object instanceof Page) {
                List resultList = ((Page) object).getContent();
                rList.addAll(resultList);
//                List results = resultList.sort(Comparator.comparing());
                String finalSortStr = sortStr;
                List results = (List) resultList.stream().sorted((e1, e2) -> {
                    Object eOne = JSON.parseObject(JSON.toJSONString(e1)).get(finalSortStr);
                    Object eNext = JSON.parseObject(JSON.toJSONString(e2)).get(finalSortStr);
                    //todo
                     if( eOne instanceof String){
                        return 1;
                     }else if(eOne instanceof Date){
                        return 1;
                    }else if(eOne instanceof Math){
                         if(eOne instanceof Integer){
                             return Integer.compare((Integer)eOne, (Integer)eNext);
                         }else{
                            return Double.compare((Double)(eOne),(Double)eNext);
                         }
                    }else {
                         return 1;
                     }
                }).collect(Collectors.toList());
                results = results.subList(startIndex,endIndex);
                initCount();
                return new PageImpl<>(resultList,pageRequest,resultList.size());
            } else {
                //不符合格式
            }
            initCount();
            return object;
        }
    }

    private static void  initCount (){
        countPageArg.set(0);
        countSortArg.set(0);
    }

    /**
     * 获取方法中的备注
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    public static PageRedis getPageRedis(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] method = targetClass.getMethods();
//        LogType[] methode = {};
        for (Method m : method) {
            if (m.getName().equals(methodName)) {
                Class[] tmpCs = m.getParameterTypes();
                if (tmpCs.length == arguments.length) {
                    PageRedis pageRedis = m.getAnnotation(PageRedis.class);
                    return pageRedis;
                }
            }
        }
        return null;
    }

    /**
     * 用于SpEL表达式解析.
     */
    private SpelExpressionParser parser = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();


    public String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod());
        Expression expression = parser.parseExpression(spELString);
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        return expression.getValue(context).toString();
    }


}